﻿/*----------------------------------------------------------------------------------------
*
*  Copyright 2019, Gao Hai Hui, <fromzeropoint@126.com>.  All rights reserved.
*  https://gitee.com/helloworldghh/xoskit.git
*  Use of this source code is governed by a MIT license
*  that can be found in the License file.
*
----------------------------------------------------------------------------------------*/
#include "../import/head.h"
#include "../data_struct/head.h"
#include "../macro/head.h"
#include "ssl_impl.h"
#include "mgr.h"

namespace xos_encrypt
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    static ssl_impl::POOL * pool_ptr = 0;

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    ssl_impl::ssl_impl()
    {
        init_data();
    }

    ssl_impl::~ssl_impl()
    {
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // public method

    int ssl_impl::get_addr( POOL *** pppPool, LIST *** pppList )
    {
        int ret = 0;
        if( pppPool )
        {
            *pppPool = &pool_ptr;
        }
        if( pppList )
        {
            *pppList = 0;
        }
        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init()
    {
        int ret = 0;
        return ret;
    }

    int ssl_impl::term()
    {
        int ret = 0;

        term_mem( &m_write_buf );
        term_mem( &m_read_buf );

        if( m_pSSL )
        {
            SSL_free( m_pSSL );
            m_pSSL = 0;
        }

        init_data();

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::get_from_mem( xos_box::i_list * pList )
    {
        int ret = 0;

        BIO * pBIO = m_pWriteBIO;
        int nMemLeftLen = ( int )BIO_ctrl_pending( pBIO );
        int nSize = xos_box::i_buf::BUF_SIZE;
        int nGetLen = 0;

        while( nMemLeftLen > 0 )
        {
            xos_box::i_buf * pBuf = mgr::buf();
            char * lpszData = pBuf->get_data( 0, 0, 0 );
            int nLen = 0;
            int nCopyLen = nMemLeftLen > nSize ? nSize : nMemLeftLen;
            nCopyLen = BIO_read( pBIO, lpszData, nCopyLen );
            nMemLeftLen -= nCopyLen;
            nGetLen += nCopyLen;
            nLen += nCopyLen;
            pBuf->set_len( nLen );
            pList->push_back( pBuf );
            pBuf = 0;
        }

        ret = nGetLen;

        return ret;
    }

    int ssl_impl::add_to_mem( xos_box::i_list * pList )
    {
        int ret = 0;

        BUF_MEM * pMem = &m_read_buf;
        BIO * pBIO = m_pReadBIO;
        int nMemLeftLen = ( int )( pMem->max - BIO_ctrl_pending( pBIO ) );
        xos_box::i_buf * pBuf = 0;
        int nAddLen = 0;

        while( ( nMemLeftLen > 0 ) && ( pBuf = ( xos_box::i_buf * )pList->front( 0 ) ) )
        {
            const char * lpszData = pBuf->get_data( 0, 0, 0 );
            int nLen = 0;
            int nPos = 0;
            pBuf->get_len( &nLen );
            pBuf->get_pos( &nPos );
            int nCopyLen = nMemLeftLen > nLen ? nLen : nMemLeftLen;
            nCopyLen = BIO_write( pBIO, lpszData, nCopyLen );
            nMemLeftLen -= nCopyLen;
            nAddLen += nCopyLen;
            nLen -= nCopyLen;
            nPos += nCopyLen;
            pBuf->set_len( nLen );
            pBuf->set_pos( nPos );
            if( 0 == nLen )
            {
                xos_stl::release_interface( pBuf );
                pList->pop_front();
            }
        }

        ret = nAddLen;

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    // > 0,  normal data
    //  0 : no error
    // -1 : error.
    //  1 : close
    int ssl_impl::get_from_ssl( xos_box::i_list * pList )
    {
        int ret = 0;

        int nSize = xos_box::i_buf::BUF_SIZE;
        int nGetLen = 0;

        while( 0 == ret )
        {
            xos_box::i_buf * pBuf = mgr::buf();
            char * lpszData = 0;
            lpszData = pBuf->get_data( 0, 0, 0 );
            int len = SSL_read( m_pSSL, lpszData, nSize );
            if( len > 0 )
            {
                nGetLen += len;
                pBuf->set_len( len );
                pList->push_back( pBuf );
                pBuf = 0;
            }
            else
            {
                int r = read_error( len );
                if( 0 != r )
                {
                    nGetLen = r;
                    ret = 1;
                }
            }
            if( pBuf )
            {
                pBuf->release();
                break;
            }
        }

        ret = nGetLen;

        return ret;
    }

    // 
    // 每次加xos_box::i_buf::BUF_SIZE-1024的长度
    // 
    int ssl_impl::add_to_ssl( xos_box::i_list * pList )
    {
        int ret = 0;

        int nMaxAddLen = xos_box::i_buf::BUF_SIZE - 1024;
        xos_box::i_buf * pBuf = 0;
        int nAddLen = 0;

        while( ( 0 == ret ) && ( nAddLen < nMaxAddLen ) && ( ( pBuf = ( xos_box::i_buf * )pList->front( 0 ) ) ) )
        {
            const char * lpszData = pBuf->get_data( 0, 0, 0 );
            int nLen = 0;
            int nPos = 0;
            pBuf->get_len( &nLen );
            pBuf->get_pos( &nPos );
            int len = nMaxAddLen - nAddLen;
            len = len > nLen ? nLen : len;
            int r = SSL_write( m_pSSL, lpszData, len );
            if( r <= 0 )
            {
                int err = write_error( r );
                if( 0 != err )
                {
                    nAddLen = -1;
                    ret = 1;
                    break;
                }
            }
            else
            {
                len = r;
            }
            nAddLen += len;
            nLen -= len;
            nPos += len;
            pBuf->set_len( nLen );
            pBuf->set_pos( nPos );
            if( 0 == nLen )
            {
                xos_stl::release_interface( pBuf );
                pList->pop_front();
            }
        }

        ret = nAddLen;

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    /*
    ERR_lib_error_string（）库函数在哪个库中
    ERR_func_error_string（）// 函数名
    ERR_reason_error_string（）// 出错的具体原因
    */
    int ssl_impl::show_error_info( int nRet )
    {
        int ret = 0;

        //int nErr = ERR_get_error();

        int nErr = SSL_get_error( m_pSSL, nRet );
        char info[4096] = { 0 };
        char * pRet = 0;

        pRet = ERR_error_string( nErr, info );

        LOG4( "openssl error : code = %d,%d, info = %s", nRet, nErr, info );

        return ret;
    }

    // 
    // return :
    //    0: no error
    //   -1: err. 
    // 
    int ssl_impl::write_error( int nRet )
    {
        int ret = -1;

        int nErr = SSL_get_error( m_pSSL, nRet );

        if( nRet < 0 )
        {
            if( SSL_ERROR_WANT_WRITE == nErr )
            {
                ret = 0;
            }
        }

        return ret;
    }

    //  0 : 成功
    // -1 : 错误
    // -2 : 关闭
    int ssl_impl::read_error( int nRet )
    {
        int ret = -1;

        int nErr = SSL_get_error( m_pSSL, nRet );

        if( 0 == nRet )
        {
            if( SSL_ERROR_ZERO_RETURN == nErr )
            {
                ret = -2;
            }
        }
        else if( nRet < 0 )
        {
            if( SSL_ERROR_WANT_READ == nErr )
            {
                ret = 0;
            }
        }

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init_null_mem( BUF_MEM * pMem )
    {
        int ret = 0;

        pMem->max = xos_box::i_buf::BUF_SIZE;
        pMem->length = 0;
        pMem->data = 0;

        return ret;
    }

    bool ssl_impl::empty_mem( BUF_MEM * pMem )
    {
        BIO * pBIO = &m_write_buf == pMem ? m_pWriteBIO : m_pReadBIO;
        int len = ( int )BIO_ctrl_pending( pBIO );

        if( pMem == &m_write_buf )
        {
            //LOG4( "write buf len = %d", len );
        }
        else
        {
            //LOG4( "read  buf len = %d", len );
        }

        return ( 0 == len );
    }

    bool ssl_impl::null_mem( BUF_MEM * pMem )
    {
        return !pMem->data;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init_mem( BUF_MEM * pMem )
    {
        int ret = 0;

        xos_box::i_buf *& pBuf = ( pMem == &m_write_buf ) ? m_pWriteBuf : m_pReadBuf;

        pBuf = mgr::buf();

        pMem->max = xos_box::i_buf::BUF_SIZE;
        pMem->length = 0;
        pMem->data = pBuf->get_data( 0, 0, 0 );

        if( pMem == &m_write_buf )
        {
            //LOG4( "init_mem : write" );
        }
        else
        {
            //LOG4( "init_mem : read" );
        }

        return ret;
    }

    int ssl_impl::term_mem( BUF_MEM * pMem )
    {
        int ret = 0;

        xos_box::i_buf *& pBuf = ( pMem == &m_write_buf ) ? m_pWriteBuf : m_pReadBuf;

        xos_stl::release_interface( pBuf );
        init_null_mem( pMem );

        if( pMem == &m_write_buf )
        {
            //LOG4( "term_mem : write" );
        }
        else
        {
            //LOG4( "term_mem : read" );
        }

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    int ssl_impl::init_data()
    {
        int ret = 0;

        init_null_mem( &m_write_buf );
        init_null_mem( &m_read_buf );

        m_bIsServer = false;
        m_bReady = false;

        m_pWriteBuf = 0;
        m_pReadBuf = 0;

        m_pWriteBIO = 0;
        m_pReadBIO = 0;

        m_pSSL = 0;

        return ret;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    //  0 : 成功
    // -1 : 错误
    int ssl_impl::shake_hand( xos_box::i_list * pIn, xos_box::i_list * pOut )
    {
        int result = 0;
        int ret = 0;

        if( ( 0 == ret ) && null_mem( &m_write_buf ) )
        {
            init_mem( &m_write_buf );
        }

        if( ( 0 == ret ) && null_mem( &m_read_buf ) )
        {
            init_mem( &m_read_buf );
        }

        while( 0 == ret )
        {
            add_to_mem( pIn );
            int r = SSL_do_handshake( m_pSSL );
            if( 1 == r )
            {
                m_bReady = true;
                ret = 1;
            }
            else
            {
                result = read_error( r );
                if( 0 != result )
                {
                    ret = 1;
                }
            }
            int len = get_from_mem( pOut );
            if( ( 0 == ret ) && ( 0 == len ) )
            {
                ret = 1;
                break;
            }
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return result;
    }

    //  0 : 成功
    // -1 : 失败
    int ssl_impl::shutdown( xos_box::i_list * pIn, xos_box::i_list * pOut )
    {
        int result = 0;
        int ret = 0;

        if( ( 0 == ret ) && null_mem( &m_write_buf ) )
        {
            init_mem( &m_write_buf );
        }

        if( ( 0 == ret ) && null_mem( &m_read_buf ) )
        {
            init_mem( &m_read_buf );
        }

        while( 0 == ret )
        {
            add_to_mem( pIn );
            int r = SSL_shutdown( m_pSSL );
            if( 1 != r )
            {
                r = SSL_shutdown( m_pSSL );
            }
            if( 1 == r )
            {
                result = 0;
                ret = 1;
            }
            else
            {
                result = read_error( r );
                if( 0 != result )
                {
                    ret = 1;
                }
            }
            int len = get_from_mem( pOut );
            if( ( 0 == ret ) && ( 0 == len ) )
            {
                ret = 1;
                break;
            }
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return result;
    }

    //  0 : 成功
    // -1 : 错误
    // -2 : 关闭
    int ssl_impl::decode( xos_box::i_list * pIn, xos_box::i_list * pOut )
    {
        int result = 0;
        int ret = 0;

        if( ( 0 == ret ) && !ready() )
        {
            result = -1;
            ret = 1;
        }

        if( ( 0 == ret ) && null_mem( &m_write_buf ) )
        {
            init_mem( &m_write_buf );
        }

        if( ( 0 == ret ) && null_mem( &m_read_buf ) )
        {
            init_mem( &m_read_buf );
        }

        while( ( 0 == ret ) && pIn->front( 0 ) )
        {
            add_to_mem( pIn );
            int len = get_from_ssl( pOut );
            if( len < 0 )
            {
                result = len;
                break;
            }
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return result;
    }

    //  0 : 成功
    // -1 : 错误
    int ssl_impl::encode( xos_box::i_list * pIn, xos_box::i_list * pOut )
    {
        int result = 0;
        int ret = 0;

        if( ( 0 == ret ) && !ready() )
        {
            result = -1;
            ret = 1;
        }

        if( ( 0 == ret ) && null_mem( &m_write_buf ) )
        {
            init_mem( &m_write_buf );
        }

        if( ( 0 == ret ) && null_mem( &m_read_buf ) )
        {
            init_mem( &m_read_buf );
        }

        while( ( 0 == ret ) && pIn->front( 0 ) )
        {
            int len = add_to_ssl( pIn );
            if( len < 0 )
            {
                result = -1;
                ret = 1;
            }
            else if( 0 == len )
            {
                ret = 1;
            }
            else
            {
                get_from_mem( pOut );
            }
        }

        if( empty_mem( &m_write_buf ) )
        {
            term_mem( &m_write_buf );
        }

        if( empty_mem( &m_read_buf ) )
        {
            term_mem( &m_read_buf );
        }

        return result;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // 

    //  服务端或客户端
    int ssl_impl::set_type( bool bServer )
    {
        int ret = 0;
        m_bIsServer = bServer;
        return ret;
    }

    int ssl_impl::initialize()
    {
        int ret = 0;

        if( 0 == ret )
        {
            void * pT = m_bIsServer ? mgr::get()->m_pServerCtx : mgr::get()->m_pClientCtx;
            SSL_CTX * pCtx = ( SSL_CTX * )pT;
            m_pSSL = SSL_new( pCtx );
            if( !m_pSSL )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            m_pWriteBIO = BIO_new( BIO_s_mem() );
            if( !m_pWriteBIO )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            m_pReadBIO = BIO_new( BIO_s_mem() );
            if( !m_pReadBIO )
            {
                ret = 1;
            }
        }

        if( 0 == ret )
        {
            BIO_set_mem_buf( m_pWriteBIO, &m_write_buf, BIO_NOCLOSE );
            BIO_set_mem_buf( m_pReadBIO, &m_read_buf, BIO_NOCLOSE );
            SSL_set_bio( m_pSSL, m_pReadBIO, m_pWriteBIO );
        }

        if( 0 == ret )
        {
            if( m_bIsServer )
            {
                SSL_set_accept_state( m_pSSL );
            }
            else
            {
                SSL_set_connect_state( m_pSSL );
            }
        }

        return ret;
    }

    bool ssl_impl::ready()
    {
        return m_bReady;
    }

} // xos_encrypt
